import threading
import re
import struct
from collections import deque
from Config import *

class MessageHandler(threading.Thread):
	def __init__(self, logger, sender, eigenTag):
		threading.Thread.__init__(self)
		self.logger = logger
		self.mSender = sender
		self.queue = deque()
		self.eigenTag = eigenTag
		# in beide dicts moeten de nieuwe clients worden toegevoegd in de ene
		# zit het adres en in de andere zit de naam. Ik neem aan dat de clients
		# die verbonden zijn aan andere servers geen adres op zich hebben dus
		# die moeten alleen in de DBN. Dit is ok aangezien de DBS alleen
		# gebruikt zou moeten worden voor namechange vanaf een client direct 
		# verbonden aan deze server.
		# keys namen
		# clientDBN staat voor client Database Names
		# key van deze database is de naam van een client
		# value is (host, port)
		self.clientDBN = dict()
		# clientDBA staat voor client Database Addresses
		# key van deze database is de host:port(bv. deze.science.uva.nl:2001)
		# value is de naam van dit adres
		self.clientDBA = dict()
		# serverDBT staat voor server Database Tags
		# key van deze database is de tag van een server
		# value is ((host, port), set(clients verbonden aan deze server))
		self.serverDBT = dict()
		# serverDBA staat voor server Database Addresses
		# key van deze database is de host:port(bv. deze.science.uva.nl:2001)
		# value is de tag van dit adres
		self.serverDBA = dict()

	def add_queue(self, data):
		self.queue.append(data)

	# create valid message struct and return the string.
	def create_message(self, type, msgno, *params):
		msg = ' '.join(params)
		if len(msg) + 6 > 200:
			raise ArgumentError("message too long!")
		return struct.pack(">hhH%ds" % len(msg), len(msg) + 6, type, msgno, msg)

	def run(self):
		while True:
			try:
				data, address = self.queue.popleft()
				analyse_data(data, address)
			except IndexError:
				pass

	# incoming data and incoming address of source/sender
	def analyse_data(self, data, address):
		# registrate client, upon failure: send 510, upon success: send 500
		def msg100(message):
			name, pw = message.split()
			if not re.match("^[A-Za-z0-9_]*$", name):
				self.logger.add_queue("Registration from %s:%s failed. " % address +
					"%s contains illegal name characters" % name, "MessageHandler")
				self.logger.add_queue("Sending 510 to %s:%s " % address, "MessageHandler")
				self.mSender.add_queue(create_message(510, 0, "Name may only consist of letters, " +
					"numbers and underscores"), address)
			else:
				# new, unique client logged in
				if not (name in self.clientDBN):
					if name in ADMINS and pw and pw != ADMINS[name]:
						# check if it was an admin who logged in
						# incorrect passw, send registration failed
						self.logger.add_queue("Sending 510 to %s:%s " % address, "MessageHandler")
						self.mSender.add_queue(create_message(510, 0, "Wrong password provided."), address)
						return
				# name already exists
				else:
					# send registration failed
					self.logger.add_queue("Sending 510 to %s:%s " % address, "MessageHandler")
					self.mSender.add_queue(create_message(510, 0, "Registration failed: name already in use."), address)
					return

			# log in succeeded, now add to the dictionary and broadcast (generate 110)
			# registration succeeded
			self.logger.add_queue("Sending 500 to %s:%s " % address, "MessageHandler")
			self.mSender.add_queue(create_message(500, 0), address)
			self.logger.add_queue("Client added with name: %s" % name, "MessageHandler")
			# add client to database
			self.clientDBN[name] = (address, 0)
			# key van deze database is de host:port(bv. deze.science.uva.nl:2001)
			self.clientDBA["%s:%s" % address] = name

			# tell all the other servers and clients.
			for client in (set(self.clientDBN.keys()) - set([name])):
				self.logger.add_queue("sending 110 about, Client added with name: %s, to %s" % (name, client), "MessageHandler")
				self.mSender.add_queue(create_message(110, 0, "000001000001%s" % name), self.clientDBN[client])
			for server in self.serverDBT.iteritems():
				self.logger.add_queue("sending 110 about, Client added with name: %s, to %s" % (name, server[0]), "MessageHandler")
				self.mSender.add_queue(create_message(110, 0, "000001000001%s" % name), self.serverDBT[server[0]][0])

		# negeer op het moment nog volgnr en lijstlengte
		def msg110(message):
			namecheck = "%s:%s" % address
			if namecheck in self.serverDBA:
				name = message[12:]
				self.serverDBT[self.serverDBA[namecheck]][1].add(name)
			for client in self.clientDBN.iteritems():
				self.logger.add_queue("sending 110 about, Client added with name: %s, to %s" % (name, client[0]), "MessageHandler")
				self.mSender.add_queue(create_message(110, 0, message), self.clientDBN[client[0]])
			for server in (set(self.serverDBT.keys()) - set([self.serverDBA["%s:%s" % address]])):
				self.logger.add_queue("sending 110 about, Client added with name: %s, to %s" % (name, server[0]), "MessageHandler")
				self.mSender.add_queue(create_message(110, 0, message), self.serverDBT[server[0]][0])

		# this function is called when the client wants to end the connection
		# call msg130 to notificate the world
		def msg120(message):
			name = self.clientDBA["%s:%s" % address]
			del self.clientDBN[name]
			del self.clientDBA["%s:%s" % address]

			if message:
				self.logger.add_queue("create 130 with greet", "MessageHandler")
				send_msg = create_message(130, 0, name, message)
			else:
				self.logger.add_queue("create 130 without greet", "MessageHandler")
				send_msg = create_message(130, 0, name)
			for client in (set(self.clientDBN.keys()) - set([self.clientDBA["%s:%s" % address]])):
				self.logger.add_queue("sending 130 about, Client removed with name: %s, to %s" % (name, client), "MessageHandler")
				self.mSender.add_queue(send_msg, self.clientDBN[client])
			for server in self.serverDBT.iteritems():
				self.logger.add_queue("sending 130 about, Client removed with name: %s, to %s" % (name, server[0]), "MessageHandler")
				self.mSender.add_queue(send_msg, self.serverDBT[server[0]][0])

	
		def msg130(message):
			name, greet = message.split(None,1)
			if name in self.clientDBN:
				clientAddress = self.clientDBN[name]
				del self.clientDBN[name]
				del self.clientDBA["%s:%s" % clientAddress]
			for client in (set(self.clientDBN.keys()) - set([self.clientDBA["%s:%s" % address]])):
				self.logger.add_queue("sending 130 about, Client removed with name: %s, to %s" % (name, client), "MessageHandler")
				self.mSender.add_queue(send_msg, self.clientDBN[client])
			for server in (set(self.serverDBT.keys()) - set([self.serverDBA["%s:%s" % address]])):
				self.logger.add_queue("sending 130 about, Client removed with name: %s, to %s" % (name, server), "MessageHandler")
				self.mSender.add_queue(send_msg, self.serverDBT[server][0])

		def msg140(message):
			self.logger.add_queue("received ping from %s:%s sending pong" % address, "MessageHandler")
			self.mSender.add_queue_priority(create_message(150, 0, message), address)

		# TODO: pinger werkend krijgen tot die tijd ontvangen we geen pongs
		def msg150(message):
			pass
			# msg =   "Sending a 150 to: "+address+" with echo request: "+data
			# logger.add_queue(msg, "MessageHandler")
			# msg = create_message(150, 0, msg)
			# mSender.add_queue(msg, address)

		# client wants to change name
		def msg160(newname):
			if address in self.clientDBA:
				oldname = self.clientDBA[address]
				if re.match("^[A-Za-z0-9_]*$", newname):
					set_of_names = set(self.clientDBN.keys())
					for server in self.serverDBT.iteritems():
						set_of_names = set_of_names | self.serverDBT[server[0]][1]
					if(newname == oldname):
						# failed to change name
						self.logger.add_queue("Sending 530 to %s:%s " % address, "MessageHandler")
						self.mSender.add_queue(create_message(530, 0, "Requested name is the same as your current name."), address)
					elif not (newname in set_of_names):
						del self.clientDBN[oldname]
						self.clientDBA[address] = newname
						self.clientDBN[newname] = address
						self.logger.add_queue("Sending 520 to %s:%s " % address, "MessageHandler")
						self.mSender.add_queue(create_message(520, 0), address)
						for client in (set(self.clientDBN.keys()) - set([name])):
							self.logger.add_queue("sending 170 about, Client changed name: %s to %s" % (oldname, newname), "MessageHandler")
							self.mSender.add_queue(create_message(170, 0, oldname, newname), self.clientDBN[client])
						for server in self.serverDBT.iteritems():
							self.logger.add_queue("sending 170 about, Client changed name: %s to %s" % (oldname, newname), "MessageHandler")
							self.mSender.add_queue(create_message(170, 0, oldname, newname), self.serverDBT[server[0]][0])
					else:
						self.logger.add_queue("Sending 530 to %s:%s " % address, "MessageHandler")
						self.mSender.add_queue(create_message(530, 0, "The requested name already exists."), address)
				else:
					self.logger.add_queue("Sending 530 to %s:%s " % address, "MessageHandler")
					self.mSender.add_queue(create_message(530, 0, "Name may only consist of letters, numbers or underscores."), address)

		def msg170(message):
			oldname, newname = message.split(None,1)
			if oldname in self.clientDBN:
				self.clientDBN[newname] = self.clientDBN[oldname]
				del self.clientDBN[oldname]
				self.clientDBA["%s:%s" % self.clientDBN[newname]] = newname
			for client in self.clientDBN.iteritems():
				self.logger.add_queue("sending 170 about, Client changed name: %s to %s" % (oldname, newname), "MessageHandler")
				self.mSender.add_queue(create_message(170, 0, oldname, newname), self.clientDBN[client[0]])
			for server in (set(self.serverDBT.keys()) - set([self.serverDBA["%s:%s" % address]])):
				self.logger.add_queue("sending 170 about, Client changed name: %s to %s" % (oldname, newname), "MessageHandler")
				self.mSender.add_queue(create_message(170, 0, oldname, newname), self.serverDBT[server][0])

		# client sent this text/action message
		def msg200_210(message, msg_type):
			destination, text = message.split(None,1)
			self.logger.add_queue("received %d for %s" % (msg_type, destination), "MessageHandler")

			if msg_type == 200:
				type = 300
			else:
				type = 310
			send_msg = create_message(type, 0, self.clientDBA[address], destination, text)

			if destination == "#all":
				for client in (set(self.clientDBN.keys()) - set([self.clientDBA["%s:%s" % address]])):
					self.logger.add_queue("sending %s, to %s" % (type, client), "MessageHandler")
					self.mSender.add_queue(send_msg, self.clientDBN[client])
				for server in self.serverDBT.iteritems():
					self.logger.add_queue("sending %s, to %s" % (type, server[0]), "MessageHandler")
					self.mSender.add_queue(send_msg, self.serverDBT[server[0]][0])
			elif destination in self.clientDBN:
				self.logger.add_queue("sending %d for %s" % (type, destination), "MessageHandler")
				self.mSender.add_queue(send_msg, self.clientDBN[destination])
			else:
				for server in self.serverDBT.iteritems():
					if destination in server[1][1]:
						self.logger.add_queue("sending %d for %s" % (type, destination), "MessageHandler")
						self.mSender.add_queue(send_msg, server[1][0])

		# server sends this text/action message to other servers and clients
		def msg300_310(message, msg_type):
			source, destination, text = message.split(None,2)
			if destination == "#all":
				for client in self.clientDBN.iteritems():
					self.logger.add_queue("sending %s, to %s" % (type, client), "MessageHandler")
					self.mSender.add_queue(create_message(msg_type, 0, message), self.clientDBN[client[0]])
				for server in (set(self.serverDBT.keys()) - set([self.serverDBA["%s:%s" % address]])):
					self.logger.add_queue("sending %s, to %s" % (type, server[0]), "MessageHandler")
					self.mSender.add_queue(create_message(msg_type, 0, message), self.serverDBT[server][0])
			elif destination in self.clientDBN:
				self.logger.add_queue("sending %d for %s" % (msg_type, destination), "MessageHandler")
				self.mSender.add_queue(create_message(msg_type, 0, message), self.clientDBN[destination])
			else:
				for server in (set(self.serverDBT.keys()) - set([self.serverDBA["%s:%s" % address]])):
					if destination in serverDBN[server][1]:
						self.logger.add_queue("sending %d for %s" % (type, destination), "MessageHandler")
						self.mSender.add_queue(create_message(msg_type, 0, message), serverDBN[server][0])

		def msg600(tag):
			self.logger.add_queue("added server with tag %s" % tag, "MessageHandler")
			self.serverDBT[tag] = (address, set())
			self.serverDBA[address] = tag
			set_of_names = set(self.clientDBN.keys())
			for server in self.serverDBT.iteritems():
				set_of_names = set_of_names | self.serverDBT[server[0]][1]
			# gebruik geen volgnrs
			map(lambda x: self.mSender.add_queue(create_message(110, 0, "000001000001%s" % x), address), set_of_names)

		def msg602_604(message, msg_type):
			if message != 'none':
				ip, port, tag = message.split(':', 2)
				self.serverDBT[tag] = ((ip, int(port)), set())
				self.serverDBA["%s:%s" % (ip, port)] = tag
				send_msg = create_message(600, 0, self.eigenTag)
				self.logger.add_queue("sending a 600 to %s:%s:%s after receiving a %d" % (ip, port, tag, msg_type), "MessageHandler")
				self.mSender.add_queue(send_msg, self.serverDBT[tag][0])

		def msg604(message):
			tag, newparent = message.split()
			try:
				del self.serverDBA[self.serverDBT[tag][0]]
				del self.serverDBT[tag]
			except KeyError:
				pass
			if newparent:
				msg602_604(newparent, 604)

		def msg700():
			for server in self.serverDBT.iteritems():
				self.logger.add_queue("sending quit message to %s" % server[0], "MessageHandler")
				self.mSender.add_queue(create_message(701, 0, "700"), server[1][0])
			for client in self.clientDBN.iteritems():
				self.logger.add_queue("sending quit message to %s" % client[0], "MessageHandler")
				self.mSender.add_queue(create_message(701, 0, "700"), self.clientDBN[1])
			self.logger.add_queue("sending quit message to controlserver", "MessageHandler")
			self.mSender.add_queue(create_message(701, 0, "700"), (CONTROL_SERVER_ADDRESS, CONTROL_SERVER_PORT))

		def msg701(reason):
			self.logger.addqueue("removing the server that is stopping for reason: %s" % reason, "MessageHandler")
			if self.serverDBA["%s:%s" % address] in self.serverDBT:
				del self.serverDBT[self.serverDBA["%s:%s" % address]]

		length, type, index, message = unpack(">hhH%ds" % (len(data)-6), data)

		# dict van berichttypen
		messages = {
			"100": msg100(message),
			"110": msg110(message),
			"120": msg120(message),
			"130": msg130(message),
			"140": msg140(message),
			"150": msg150(message),
			"160": msg160(message),
			"170": msg170(message),
			"200": msg200_210(message, 200),
			"210": msg200_210(message, 210),
			"300": msg300_310(message, 300),
			"310": msg300_310(message, 310),
			"600": msg600(message),
			"602": msg602_604(message, 602),
			"604": msg604(message),
			"700": msg700(),
			"701": msg701(message)
		}

		# dict lookup om berichten af te handelen
		messages[type]
